//////////////////////////////////////////////////////////////////////////////////
//	Copyright 2011 by Yosef Grabivker, ryuho@homenetworksoft.com
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//////////////////////////////////////////////////////////////////////////////////

#include <wx/wx.h>

#include <time.h>
#include <algorithm>
#include <iostream>
#include <fstream>

#include "logicmanager.h"
#include "linktorouter.h"
#include "mainwnd.h"
#include "guilogic.h"
#include "logviewer.h"

#include "syslogsrv.h"
#include "rtsocket.h"
#include "sysdepapi.h"
#include "../smtp/wxsmtp.h"
#include "progressdlg.h"
#include "routercaps.h"


#define MAX_NOANSWEAR_TIME 3*60

bool CGUILogic::m_bSortAsc = false;

CGUILogic::CGUILogic(CMainWnd& wMain):
	m_pLink(NULL), m_wMain(wMain), m_scriptGen( m_wMain.GetConfig() ), m_tUpdateInterval(NORMAL_UPDATE_SPEED),
	m_lMessageCount(0), m_lMessageCountCopy(0), m_lMessageCountEq(0),m_nThreadCount(0), m_pLogDlg(NULL),
	m_pSysLogSrv(NULL), m_nLogSortCol(cLogColumnTime), m_bAutoConnect(true), m_smtp(NULL), m_tLastSMTPSendTime(0),
	m_tLastWANIPCheckTime(0), m_stateController(*this),	m_tLastReboot(0), m_logSaverSysLog(*this),
	m_logSaverAppLog(*this), m_tLastStatus(0), m_pProgress(NULL),m_pTheTaskBarIcon(NULL)
{
	m_pTheTaskBarIcon = new CTaskBarIcon(m_wMain);
}

CGUILogic::~CGUILogic()
{
	DestroyProgress();

	CleanUp();

	PrepareToProgramExit();
}

void CGUILogic::Init()
{
	Load();

	UpdateTaskBarIcon();
	UpdateStatusBar();

	m_bAutoPortForward = m_wMain.GetPropStorage().GetAutoPortForward();
	m_lstPortForward  = m_wMain.GetPropStorage().GetPortForwardLst();

	Start(10*TICK_TIME);

	LOGENTRY entry;
	entry.strCustomEvent = _("Program started");
	AddToLog(entry);

	StartSysLogServer();

	m_smtp = new wxSMTP(NULL);

	UpdatePortForwardGUI();

	GUIBIND bind;
	bind.bEnabled	= false;
	bind.codeMaxVal = cCmdFlushRTTable;
	bind.codeVal    = cCmdFlushRTTable;
	bind.tUpdateDelay=60*60;/*1h*/
	AddBind( bind );
}

void CGUILogic::CleanUp()
{
	if ( IsRunning() )
	{
		Stop();
	}
	
	DestroyLinkToRouter();

	StopSysLogServer();

	if ( m_smtp )
	{
		m_smtp->Destroy();

		//delete done internally!!!!
		//delete m_smtp;

		m_smtp = NULL;
	}

	PrepareToProgramExit();
}

void CGUILogic::Refresh()
{
	if ( m_pLink )
	{
		//Enable oneshots here, to correct Refresh logic
		EnableCmd(cCmdGetIPConfig);

		Tick(true);
	}
	else
	{
		//m_wMain.InitRouterConnection();
	}
}

void CGUILogic::Refresh(PANELTYPE pl)
{
	if ( m_pLink )
	{
		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];
				
			if ( pl == bind.plType )
			{
				COMMANDREQUEST req(bind.codeVal);
				m_pLink->AddRequest(req);
				break;
			}
		}
	}
	else
	{
		m_wMain.InitRouterConnection();
	}
}

void CGUILogic::SetUpdateSpeed(time_t	tVal)
{
	m_tUpdateInterval=tVal;

	//Save update interval
	Save();
}

void CGUILogic::Save()
{

}

void CGUILogic::Load()
{

}



//////////////////////////////////////////////////////////////////////////////////////////
//Logic	
void CGUILogic::AddBind(GUIBIND& bind)
{
	bind.tLastUpdate=0;
		
	SetPropData(bind);
		
	m_binds.push_back(bind);
}

bool CGUILogic::GetBind( GUIBIND& bindOut, const IGUIBind* pBIND )
{
	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];
		
		if ( pBIND == bind.GetBinding()  )
		{
			bindOut = bind;
			
			return true;
		}
	}
	
	return false;
}

bool CGUILogic::GetBind( GUIBIND& bindOut, cCmdCode code )
{
	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];
		
		if ( code == bind.codeMaxVal ||
			 code == bind.codeVal )
		{
			bindOut = bind;
			
			return true;
		}
	}
	
	return false;
}

void CGUILogic::ProcessCmd()
{
	try
	{
		if ( m_pLink )
		{
			COMMANDRESPONSE cmd;
			
			time_t tNow =	time(NULL);

			bool bTick(false);
			
			while ( m_pLink &&
				    m_pLink->GetResponce(cmd) )
			{
				if ( !m_lMessageCount++ )
				{
					LOGENTRY entry;
					entry.strCustomEvent = _("Router connection established");
					AddToLog(entry);

					wxString str=_T("Connected to ");
					str+=m_params.strIP;
					m_wMain.SetPrgTitle(str);
					m_wMain.UpdateHelpPage();
				}

				if ( tNow - m_tLastCmdReceived>MAX_NOANSWEAR_TIME )
				{
					LOGENTRY entry;
					entry.strCustomEvent = _("Router not answear to requests, try to reconnect");
					AddToLog(entry);

					DestroyLinkToRouter(0);
				}

				OnController(cmd);

				if ( cCmdTick == cmd.code )
				{
					bTick = true;
				}

				bool bHandled(false);

				DispatchRouterResponse(cmd, bHandled);

				if (cmd.strData.size()>10 || cmd.lData>0 )
				{
					m_tLastCmdReceived = tNow;
				}

				//Handle GUI Binds
				if ( !bTick && !bHandled )
				{
					for(size_t i=0;i<m_binds.size();i++)
					{
						GUIBIND& bind = m_binds[i];

						if ( bind.codeMaxVal == cmd.code ||
							 bind.codeVal == cmd.code )
						{
							if ( bind.GetBinding() )
							{
								try
								{
									bind.GetBinding()->OnResponse(cmd);
								}catch(...) {}
							}
						}

						if ( bind.codeMaxVal == cmd.code )
						{
							bind.tLastUpdate = tNow;
							bind.SetMaxVal( cmd.lData );
							
							//Have max and cur in one shot
							if ( bind.codeMaxVal==bind.codeVal )
							{
								bind.SetVal( cmd.lData1 );
							}

							if ( !cmd.strData.empty() )
							{
								wxString str = wxString::FromAscii( cmd.strData.c_str() );

								OnTextCMD( cmd.code, str );

								bind.SetTxtVal( str );

								if ( bind.bOneShort )
								{
									bind.bEnabled = false;
								}
							}
						}
						else if ( bind.codeVal == cmd.code )
						{
							bind.tLastUpdate = tNow;
							
							unsigned long lMax = bind.GetMaxVal();
							
							if ( lMax )
							{
								//previous value
								unsigned char btCurLevel    = (unsigned char)((100*bind.GetVal())/lMax);

								//new value
								unsigned char btNewCurLevel = (unsigned char)((100*cmd.lData)/lMax);
								
								if ( IsSameLevel(bind, btCurLevel, btNewCurLevel) )
								{
									bind.lLevelCount++;
								}
								else
									bind.lLevelCount=0;
								
								bind.SetVal( cmd.lData );

								DoAction( bind, btNewCurLevel );
							}
						}
					}
				}
			}

			UpdateTaskBarIcon();
			UpdateStatusBar();

			if (bTick) Tick();
		}
	}catch(...)
	{

	}
}

void CGUILogic::DispatchRouterResponse(const COMMANDRESPONSE& cmd, bool& bHandled)
{
	bHandled = true;

	switch(cmd.code)
	{
		case cCMDRouterReady:
		{
			DoStartUpAction();
		}
		break;

		case cCmdSetWirelessCaps:
		{
			CRouterCaps::GetIntstance().SetAPCaps( cmd.strData.c_str() );
			OnWirelessCaps(cmd.strData);
			m_wMain.OnRouterConnected();
		}
		break;

		case cCmdGetRouterWANAddress:
		{
			if ( !cmd.strData.empty() )
			{
				m_tLastWANIPCheckTime = time(NULL);
				OnWANAddress( cmd.strData );
			}
		}
		break;

		case cCmdGetTCPTimeOuts:
		{
			m_natTimeOuts = cmd.natTimeOuts;
		}
		break;

		case  cCmdGetVersion:
		{
			OnRouterVersion(cmd.strData);
		}
		break;

		case cCmdScript:
		{
			OnScriptResult(cmd.subCode, cmd.strData);
		}
		break;

		case cCmdGetProcessList:
		{
			OnProcList(cmd.strData);
		}
		break;

		case cCmdSetModemCaps:
		{
			CRouterCaps::GetIntstance().SetModemCaps( cmd.strData.c_str() );
			m_strModemCaps = cmd.strData;
			m_wMain.OnRouterConnected();
		}
		break;

		case cCmdExecuteModemXML:
		{
			DestroyProgress();
		}
		break;

		case cCMDShellCaps:
		{
			CRouterCaps::GetIntstance().SetShellCaps( cmd.strData.c_str() );
			m_scriptGen.SetSciptType( CRouterCaps::GetIntstance().HasXMLScriptFormat() );

			//We can build GUI according to router 
			m_wMain.OnRouterConnected();
		}
		break;

		default:
			bHandled = false;
	}
}

void CGUILogic::OnController(const COMMANDRESPONSE& cmd)
{
	if ( CRouterCaps::GetIntstance().HasNATUsageInfo() &&
		 CRouterCaps::GetIntstance().HasMEMUsageInfo() &&
		 CRouterCaps::GetIntstance().HasRTUsageInfo() )
	{
		PROPENTRY pp;
		m_wMain.GetPropStorage().GetProperty(cPTNAT, pp);
		m_stateController.SetEnableNATRepair( pp.bEnableRepair );

		RTSTRING strScipt;

		bool bForce(false);

		m_stateController.OnCMD(cmd, strScipt, m_wMain.GetPropStorage().GetEnableNATSizeOpt(), bForce );

		//Set Routing cache values from slab table
		if ( cCmdGetSlabTable == cmd.code )
		{
			GUIBIND bindOut;

			if ( GetBind(bindOut, cCmdRTCacheMax ) )
			{
				bindOut.GetBinding()->SetVal(  m_stateController.GetCurRTCacheSize() );
			}

			UpdateStatusBar();
		}


		if ( !strScipt.empty() &&
			( m_wMain.GetPropStorage().GetEnableNATSizeOpt() || bForce ) )
		{
			if ( m_pLink )
			{
				COMMANDREQUEST req( cCmdScript );
				req.strScript = wxString::FromAscii( strScipt.c_str() );
				m_pLink->AddRequest(req);
			}

			RequestMaxFromAll();
		}

		if ( m_stateController.IsChanged() )
		{
			RTSTRING str = m_stateController.BuildStateXML();
			GUIBIND bindOut;

			if ( GetBind(bindOut, cInternalCmdGetPerfomanceTab ) )
			{
				bindOut.GetBinding()->SetVal( wxString::FromAscii(str.c_str()) );
			}
		}
	}
}

void CGUILogic::ProcessSysLogCmd()
{
	if ( m_pSysLogSrv )
	{
		COMMANDRESPONSE cmd;
		
		while ( m_pSysLogSrv->GetResponce(cmd) )
		{
			if ( m_wMain.GetPropStorage().GetSysLogFromRouterOnly() )
			{
				if ( !CRTSocket::IsAddressEqual( m_params.strIP.ToAscii(), cmd.msg.strHost.ToAscii() ) )
				{
					continue;
				}
			}

			//Clear syslog copy if have to
			if ( m_syslogCopy.size()>MAX_SYS_LOG_SIZE)
			{
				SYSLOGMSGSTORE::iterator iEnd = m_syslogCopy.begin();
				advance( iEnd, MAX_SYS_LOG_SIZE/3 );
				m_syslogCopy.erase( m_syslogCopy.begin(), iEnd );
			}

			//Add message to syslog copy
			m_syslogCopy.push_back(cmd.msg);

			//Save msg to log
			if ( m_wMain.GetPropStorage().GetEnableSyslogSave() )
			{
				SaveSyslogMsg( cmd.msg );
			}

			OnSysLogMessage( cmd.msg );

			for(size_t i=0;i<m_binds.size();i++)
			{
				GUIBIND& bind = m_binds[i];

				if ( bind.codeMaxVal == cmd.code ||
					 bind.codeVal == cmd.code )
				{
					try
					{
						bind.GetBinding()->OnResponse(cmd);
					}catch(...) {}
				}
			}
		}
	}
}

void CGUILogic::UpdateTaskBarIcon()
{
	if ( m_pTheTaskBarIcon )
	{
		wxString strTool = GenerateUsageToolTip();

		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];

			if ( m_pTheTaskBarIcon->GetType()==bind.plType )
			{
				m_pTheTaskBarIcon->SetIcon( bind, strTool );
				return;
			}
		}

		//Happens if router does not have NAT
		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];

			if ( bind.plType!=cPTNone )
			{
				m_pTheTaskBarIcon->SetIcon( bind, strTool );
				return;
			}
		}

		//Update icon anywhere
		GUIBIND bind;
		m_pTheTaskBarIcon->SetIcon( bind, strTool );
	}
}

void CGUILogic::UpdateStatusBar()
{
	wxStatusBar* pBar = m_wMain.GetStatusBar();

	if ( pBar )
	{
		int nItemsToShow = 0;

		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];
			if (bind.plType==cPTNone) continue;

			nItemsToShow++;
		}

		if ( nItemsToShow )
		{
			if ( pBar->GetFieldsCount()!=nItemsToShow )
			{
				//Rearrange status bar
				int nCount = 0;
				int* pSizes = new int[m_binds.size()];

				for(size_t i=0;i<m_binds.size();i++)
				{
					GUIBIND& bind = m_binds[i];
					if (bind.plType==cPTNone) continue;

					if (bind.plType == cPTCPU )
						pSizes[nCount]=100;
					else
						pSizes[nCount]=230;

					nCount++;
				}
		
				if ( nCount )
				{
					pBar->SetFieldsCount(nCount, pSizes );
				}

				delete [] pSizes;
			}

			int nIdx =0;

			for(size_t i=0;i<m_binds.size();i++)
			{
				GUIBIND& bind = m_binds[i];
				if (bind.plType==cPTNone) continue;

				wxString str = GenerateStatusString(bind);

				//Force to clear
				pBar->SetStatusText(wxT(""), nIdx);
				pBar->SetStatusText(str, nIdx++);
			}
		}
	}
}

wxString CGUILogic::GenerateStatusString(GUIBIND& bind)
{
	wxString str=PanelToName( bind.plType );
	str+=wxT(":");
	//str+=_("Usage");
	//str+=_(":");
	str+=wxT(" ");

	if ( !bind.GetMaxVal() )
	{
		str+=_("n/a");
	}
	else
	{
		switch(bind.plType)
		{
			case cPTCPU:
			{
				str+=wxString::Format(wxT("%u%%"), (bind.GetVal()*100)/bind.GetMaxVal() );
			}
			break;

			default:
			{
				str+=wxString::Format(wxT("%u/%u [%u%%]"), bind.GetVal(),bind.GetMaxVal(), (bind.GetVal()*100)/bind.GetMaxVal() );
			}
			break;
		}
	}

	return str;
}

void  CGUILogic::EnableCmd( cCmdCode cmd, bool bEnable )
{
	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];

		if ( cmd == bind.codeMaxVal ||
			 cmd == bind.codeVal )
		{
			bind.bEnabled = bEnable;
			break;
		}
	}
}

void CGUILogic::SetCMDUpdateDelay( cCmdCode cmd, time_t tVal )
{
	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];

		if ( cmd == bind.codeMaxVal ||
			cmd == bind.codeVal )
		{
			bind.tUpdateDelay = tVal;
			break;
		}
	}
}

wxString CGUILogic::GenerateUsageToolTip()
{
	wxString str;
	wxString strTool;

	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];

		if (bind.plType==cPTNone) continue;

		if ( !str.IsEmpty() )
		{
			str+=wxT("\n");
		}

		strTool = PanelToName( bind.plType );
		strTool+=wxT(": ");

		if ( bind.GetMaxVal() )
		{
			unsigned long lUsage = (100*bind.GetVal())/bind.GetMaxVal();
			strTool+= wxString::Format(wxT("%.2u%%"), lUsage);
		}
		else
		{
			strTool+= wxString::Format(wxT("%s"), _("n/a"));
		}

		str+=strTool;
	}

	return str;
}

wxString CGUILogic::PanelToName(PANELTYPE pt)
{
	wxString str;

	switch ( pt )
	{
		case cPTNAT:
			str = _("NAT");
		break;

		case cPTMem:
			str = _("Memory");
		break;

		case cPTRoute:
			str = _("Routing");
		break;

		case cPTCPU:
			str = _("CPU");
		break;

		default:
		break;
	}

	return str;
}

wxString CGUILogic::StratToName(RECOVERYSTRAT strat)
{
	wxString str;

	switch ( strat )
	{
		/*
		case cRCACTGrow:
			str = _("Do Grow");
		break;
		*/

		case cRCACTFlush:
			str = _("Do Flush");
		break;

		case cRCACTReboot:
			str = _("Do Reboot");
		break;

		case cRCACTScript:
			str = _("Do Script");
		break;

		default:
		break;
	}

	return str;
}

void CGUILogic::Tick(bool bForce)
{
	if ( m_pLink )
	{
		time_t tNow =	time(NULL);
		
		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];

			CheckPropChanges(bind);

			if (!bind.bEnabled) continue;

			if (!IsCMDSupported(bind.codeMaxVal) ) continue; 
			if (!IsCMDSupported(bind.codeVal) ) continue; 

			//if doesn't have max val request it
			if ( !bind.GetMaxVal() && bind.plType!=cPTNone )
			{
				COMMANDREQUEST req(bind.codeMaxVal);
				m_pLink->AddRequest(req);
				
				//request value also
				if ( bind.codeMaxVal!=bind.codeVal )
				{
					req.code = bind.codeVal;
					m_pLink->AddRequest(req);
				}
			}
			else
			if ( (tNow>=bind.tLastUpdate+bind.tUpdateDelay+GetUpdateIntervl(bind.codeVal)) || bForce )
			{
				COMMANDREQUEST req(bind.codeVal);
				PrepareRequest(req);
				m_pLink->AddRequest(req);
			}
		}

		if ( tNow - m_tLastWANIPCheckTime>CHECK_WAN_ADDRESS_INT )
		{
			//tmp
			COMMANDREQUEST req(cCmdGetRouterWANAddress);
			m_pLink->AddRequest(req);
		}

		long lInt = m_wMain.GetPropStorage().GetAppLogStatusInterval();

		if ( lInt!=STATUS_LOG_DISABLED &&
			 tNow - m_tLastStatus> lInt )
		{
			GenerateStatusLog();

			m_tLastStatus = tNow;
		}

		//must be false==
		if ( m_rebootSched.bEnableSched &&
			 false == m_rebootSched.IsBlockedByScheduler() )
		{
			time_t tNow = time(NULL);

			if ( tNow-m_tLastReboot>60*60 )
			{
				m_tLastReboot = tNow;
				DoRouterReboot( _("Rebooted by scheduler") );
			}
		}
	}
}

bool CGUILogic::IsCMDSupported(CMDCODE code) const
{
	bool bRes(true);

	switch(code)
	{
		case cCmdPing:
		{
			bRes = CRouterCaps::GetIntstance().HasPing();
		}
		break;

		case cCmdConntracMax:
		case cCmdGetSlabTable:
		{
			bRes = CRouterCaps::GetIntstance().HasNATUsageInfo();
		}
		break;

		case cCmdMemMax:
		case cCmdMemVal:
		{
			bRes = CRouterCaps::GetIntstance().HasMEMUsageInfo();
		}
		break;

		case cCmdRTCacheMax:
		case cCmdRTCacheVal:
		{
			bRes = CRouterCaps::GetIntstance().HasRTUsageInfo();
		}
		break;

		case cCmdCPU:
		{
			bRes = CRouterCaps::GetIntstance().HasCPUUsageInfo();
		}
		break;

		case cCmdConntrTableEntries:
		{
			bRes = CRouterCaps::GetIntstance().HasConnectionsList();
		}
		break;

		case cCmdInterfaceStat:
		{
			bRes = CRouterCaps::GetIntstance().HasBandwidth();
		}
		break;
	}

	return bRes;
}

void CGUILogic::PrepareRequest(COMMANDREQUEST& req)
{
	switch(req.code)
	{
		case cCmdPing:
		{
			req.strScript = GeneratePingScript();
		}
		break;
	}
}

//Check thread health
void CGUILogic::Notify()
{
	if ( m_lMessageCountCopy == m_lMessageCount )
	{
		m_lMessageCountEq++;
	}
	else
	{
		m_lMessageCountCopy = m_lMessageCount;
		m_lMessageCountEq = 0;
	}
	
	
	try
	{
		if ( m_params.IsValid() )
		{
			if ( !m_pLink )
			{
				m_lMessageCountEq = 0;

				if ( m_bAutoConnect )
				{
					StartLinkToRouter(m_params);
				}
			}
			else if ( m_lMessageCountEq>IDLE_THREAD_MAX && m_nThreadCount<MAX_WORK_THREADS )
			{
				//delete m_pLink;
				
				COMMANDREQUEST cmd;
				cmd.code = cCmdQuit;
				m_pLink->AddRequest(cmd);
				
				m_pLink = NULL;
				
				m_lMessageCountEq = 0;
				
				if ( m_bAutoConnect )
				{
					StartLinkToRouter(m_params);
				}
			}
		}
	}
	catch(...)
	{

	}

	SendLogToSMTP();

	ProcessAutoForwarding();

	CheckScheduling();
}

void CGUILogic::DoAction( GUIBIND& bind, unsigned char btCurLevel )
{
	if ( bind.lLevelCount>=DEF_STABLE_TICK )
	{
		if ( IsWarningLevel(bind, btCurLevel) )
		{
			ProcessAction(bind, true);

			if ( bind.dataEntry.onWarning!=cRCACTNothing )
			{
				bind.lLevelCount	= 0;
			}

			bind.bWarningFaired = true;
		}
		else if ( IsAlarmLevel(bind , btCurLevel) )
		{
			//Run Warning action prior if not ran
			if ( !bind.bWarningFaired &&
				 bind.dataEntry.onWarning!=cRCACTNothing )
			{
				ProcessAction(bind, true);
				bind.bWarningFaired = true;
			}
			else
			{
				ProcessAction(bind, false);
				bind.bWarningFaired = false;
			}

			bind.lLevelCount = 0;
		}
	}
}

void CGUILogic::LogAction(const GUIBIND& bind, bool bWarning)
{
	RECOVERYSTRAT strat = bWarning?bind.dataEntry.onWarning:bind.dataEntry.onAlarm;

	if ( cRCACTNothing!=strat )
	{
		LOGENTRY entry;

		entry.plType = bind.plType;
		entry.strat	 = strat;
		

		AddToLog(entry);
	}
}

void CGUILogic::AddToLog(LOGENTRY entry, time_t tNexAddInterval/*=0*/)
{
	bool bAdd(true);

	entry.tEvent = time(NULL);

	if ( m_log.size()>MAX_LOG_RECORDS )
		m_log.erase(m_log.begin());

	if ( tNexAddInterval!=0 )
	{
		time_t tNow = time(NULL);

		for ( size_t i=0;i<m_log.size();++i )
		{
			LOGENTRY& entryLog = m_log[i];

			if ( entryLog.strCustomEvent == entry.strCustomEvent &&
				 tNow - entryLog.tEvent<=tNexAddInterval )
			{
				bAdd = false;
				break;
			}
		}
	}
	
	if ( bAdd )
	{
		m_log.push_back(entry);
		SortLog( m_nLogSortCol );

		if ( false==entry.bDoNotSave )
		{
			SaveLogEntry(entry);
		}

		if (m_pLogDlg) m_pLogDlg->DataChanged();
	}
}

void  CGUILogic::SaveLogEntry(const LOGENTRY& entry)
{
	wxString strRecord, str;

	wxDateTime dt(entry.tEvent);
	str = dt.Format( wxT("%c"));

	strRecord+=str;
	strRecord+=wxT("\t");

	if ( entry.strCustomEvent.IsEmpty() )
	{
		str = wxString::Format(wxT("%-12s"), CGUILogic::PanelToName( entry.plType ).c_str());
	}
	else
	{
		str = wxString::Format(wxT("%-12s"), _("Application"));
	}

	if ( entry.bStatus )
	{
		str = wxString::Format(wxT("%-12s"), _("Status"));
	}

	strRecord+=str;
	strRecord+=wxT("\t");

	if ( entry.strCustomEvent.IsEmpty() )
	{
		str = CGUILogic::StratToName( entry.strat );
	}
	else
	{
		str = entry.strCustomEvent;
	}

	strRecord+=str;

	m_logSaverAppLog.SetLogParams( _("Application log"), 
		m_wMain.GetPropStorage().GetAppLogSaveFileName(),
		m_wMain.GetPropStorage().GetAppLogSaveLimit() );


	m_logSaverAppLog.AddLogRecord( strRecord );
}

void CGUILogic::GenerateStatusLog()
{
	if ( IsConnected() )
	{
		m_wMain.GenerateStatusLog();
	}
}

void CGUILogic::ProcessAction(const GUIBIND& bind, bool bWarning)
{
	wxString str;

	LogAction(bind, bWarning);
		
	switch ( bWarning?bind.dataEntry.onWarning:bind.dataEntry.onAlarm )
	{
		/*
		case cRCACTGrow:
		{
			COMMANDREQUEST req( cCmdGrow );
			req.type = bind.plType;
			m_pLink->AddRequest(req);
			RequestMaxFromAll();
		}
		break;
		*/
		
		case cRCACTFlush:
		{
			str = MakeFlushAction();
			m_stateController.OnFlushAction( bind.plType);
		}
		break;
		
		case cRCACTReboot:
		{
			str = MakeRebootAction();
			SetAllValuesToZero();
			m_stateController.OnRebootAction( bind.plType);
		}
		break;
		
		case cRCACTScript:
		{
			if ( bWarning )
			{
				str = bind.dataEntry.onWarningScript;
			}
			else
			{
				str = bind.dataEntry.onAlarmScript;
			}
			
			RequestMaxFromAll();
		}
		break;
		
		default:
		break;
	}
	
	if ( !str.IsEmpty() )
		SendActionToRouter(str);
}

wxString CGUILogic::MakeFlushAction()
{
	return wxString::FromAscii(CMD_FLUSH_RT_CACHE);
}

wxString CGUILogic::MakeRebootAction()
{
	return CMD_REBOOT;
}


void CGUILogic::SendActionToRouter(const wxString& str, CMDSUBCODE subCode)
{
	if ( m_pLink )
	{
		if ( !str.IsEmpty() )
		{
			COMMANDREQUEST req( cCmdScript, subCode );
			req.strScript = str;
			
			m_pLink->AddRequest(req, true);
		}
	}
}

void CGUILogic::SendCMDToRouter(cCmdCode code, const wxString& str,
								CMDSUBCODE subCode, bool bShowADSLProgres/*=true*/)
{
	if ( m_pLink )
	{
		DestroyProgress();

		if ( bShowADSLProgres && subCode==cCmdSubCodeADSLTweak )
		{
			m_pProgress = CProgressDlg::CreateProgressDlg(&m_wMain, _("Operation in progress, sending commands to the router"));
		}

		if ( !str.IsEmpty() )
		{
			COMMANDREQUEST req( code, subCode );
			req.strScript = str;
			
			m_pLink->AddRequest(req);
		}
	}
}

void CGUILogic::RequestMaxFromAll()
{
	if ( m_pLink )
	{
		for(size_t i=0;i<m_binds.size();i++)
		{
			GUIBIND& bind = m_binds[i];

			if ( bind.codeMaxVal!=bind.codeVal )
			{
				COMMANDREQUEST req( bind.codeMaxVal );
				m_pLink->AddRequest(req);

				COMMANDREQUEST reqVal( bind.codeVal );
				m_pLink->AddRequest(reqVal);
			}
		}
	}
}

void CGUILogic::SaveSyslogMsg( const SYSLOGMSG& msg )
{
	m_logSaverSysLog.SetLogParams( _("Syslog log"), 
		m_wMain.GetPropStorage().GetSyslogSaveFileName(),
		m_wMain.GetPropStorage().GetSyslogSaveLimit() );

	m_logSaverSysLog.AddLogRecord( msg.strRaw );
}

void CGUILogic::OnSysLogMessage( const SYSLOGMSG& msg )
{
	SYSLOGACTIONS actions =	m_wMain.GetPropStorage().GetSysLogActions();

	for (SYSLOGACTIONS::iterator iCur=actions.begin();
		iCur!=actions.end();iCur++)
	{
		SYSLOGACTION& action = iCur->second;

		if ( cRCACTNothing == action.recvStrat )
			continue;

		if ( msg.strRaw.Matches( action.strPattern) ||
			 wxNOT_FOUND !=msg.strRaw.Find(action.strPattern) )
		{
			wxString strReason(_T("Do Action: "));

			switch(action.recvStrat)
			{
				case cRCACTReboot:
				{
					strReason = _("Reboot.");
				}
				break;

				case cRCACTScript:
				{
					strReason = _("Run.");
				}
				break;
			}

			strReason+=_("Reason: Syslog message received");
			strReason+=wxT("[");
			strReason+=msg.strRaw;
			strReason+=wxT("]");

			switch(action.recvStrat)
			{
				case cRCACTReboot:
				{
					DoRouterReboot( strReason );
				}
				break;

				case cRCACTScript:
				{
					if ( !action.strScriptName.empty() )
					{
						wxExecute(  action.strScriptName, wxEXEC_ASYNC );
					}
				}
				break;
			}
		}
	}
}

void CGUILogic::DoRouterReboot(const wxString& strReason)
{
	LOGENTRY entry;
	entry.plType = cPTNone;
	entry.strat	 = cRCACTReboot;
	entry.strCustomEvent = strReason;
	
	AddToLog(entry);

	SendActionToRouter( MakeRebootAction() );
}

///////////////////////////////////////////////////////////////////////////////////////
void CGUILogic::SetPropData(GUIBIND& bind)
{
	if ( bind.pStorage )
	{
		bind.pStorage->GetProperty(bind.plType, bind.dataEntry);
		bind.SetWarningLevel( bind.dataEntry.btWarningLevel );
		bind.SetAlarmLevel( bind.dataEntry.btAlarmLevel );
	}
}

void CGUILogic::CheckPropChanges(GUIBIND& bind)
{
	if ( bind.pStorage )
	{
		if ( bind.pStorage->IsChanged(bind.plType, bind.dataEntry) )
		{
			SetPropData(bind);
		}
	}
}

bool CGUILogic::IsSameLevel(const GUIBIND& bind, unsigned char btCurLevel,
		unsigned char btNewCurLevel)
{
	if ( btCurLevel==btNewCurLevel ) return true;
	
	if ( IsNormalLevel(bind, btCurLevel) &&
		 IsNormalLevel(bind, btNewCurLevel) )
	{
		return true;
	}
	
	if ( IsWarningLevel(bind, btCurLevel) &&
		 IsWarningLevel(bind, btNewCurLevel) )
	{
		return true;
	}
	
	if ( IsAlarmLevel(bind, btCurLevel) &&
		 IsAlarmLevel(bind, btNewCurLevel) )
	{
		return true;
	}
	
	return false;
}

bool CGUILogic::IsNormalLevel(const GUIBIND& bind, unsigned char btCurLevel)
{
	if ( btCurLevel<bind.dataEntry.btWarningLevel )
	{
		return true;
	}
	
	return false;
}

bool CGUILogic::IsWarningLevel(const GUIBIND& bind, unsigned char btCurLevel)
{
	if ( btCurLevel>=bind.dataEntry.btWarningLevel &&
		 btCurLevel<bind.dataEntry.btAlarmLevel )
	{
		return true;
	}
	
	return false;
	
}

bool CGUILogic::IsAlarmLevel(const GUIBIND& bind, unsigned char btCurLevel)
{
	if ( btCurLevel>=bind.dataEntry.btAlarmLevel)
	{
		return true;
	}
	
	return false;
}

//////////////////////////////////////////////////////////////////////////////////////////
//Thread
void CGUILogic::StartLinkToRouter(const CONPARAMS& params)
{
	m_lMessageCount = 0;

	m_tLastCmdReceived = time(NULL);

	m_params = params;
	m_params.strModemSettings =  m_wMain.GetConfig().LoadADSLXML();

	if ( m_pLink ) DestroyLinkToRouter();
	
	m_pLink = new  CLinkToRouter(m_wMain);
	
	if ( !m_pLink ) THROW_EXC(cERNoFreeMem);

	wxString str=_T("Connecting to: ");
	str+=m_params.strIP;
	m_wMain.SetPrgTitle(str);

	m_pLink->CreateLink(m_params);
	
	m_nThreadCount++;
}

void CGUILogic::DestroyLinkToRouter(int nReason)
{
	//OutputDebugStr(_T("CGUILogic::DestroyLinkToRouter() Begin()\n"));

	CRouterCaps::GetIntstance().Reset();

	DestroyProgress();

	m_strModemCaps = "";

	SetAllValuesToZero();

	m_tLastCmdReceived = time(NULL);

	if ( m_pLink )
	{
		m_strSessLog = m_pLink->GetLog();

		m_nThreadCount--;
		
		CLinkToRouter* pLink = m_pLink;
		m_pLink = NULL;

		delete pLink;
		
		LOGENTRY entry;

		if ( m_lMessageCount )
		{
			entry.strCustomEvent = _("Router connection destroyed");
			AddToLog(entry);
		}
		else
		{
			entry.strCustomEvent = _("Not connected");

			if ( cERNone!=nReason )
			{
				entry.strCustomEvent+=wxT(": ");
				entry.strCustomEvent+= CRTException::FormatReason( (cExcReason)nReason );
			}

			AddToLog(entry);
			
			wxString str=entry.strCustomEvent;
			m_wMain.SetPrgTitle(str);
		}
	}

	m_lMessageCount = 0;

	OutputDebugStr(_T("CGUILogic::DestroyLinkToRouter() End()\n"));
}

bool CGUILogic::IsConnected()
{
	bool bRes(false);

	if ( m_pLink && m_lMessageCount>0 )
	{
		bRes = true;
	}

	return bRes;
}

void CGUILogic::ReInitSysLogSrv()
{
	StopSysLogServer();
	StartSysLogServer();
}

void CGUILogic::StartSysLogServer()
{
	try
	{
		if ( m_pSysLogSrv ) StopSysLogServer();

		int nUdpPort = m_wMain.GetPropStorage().GetSysLogUDPPort();

		m_pSysLogSrv = new CSysLogSrv(m_wMain, *this, nUdpPort);

		if (!m_pSysLogSrv) THROW_EXC(cERNoFreeMem);

		m_pSysLogSrv->Start();
	}
	catch(...)
	{
		LOGENTRY entry;
		entry.strCustomEvent = _("Failed to create syslogd server.");
		AddToLog(entry);
	}
}

void CGUILogic::StopSysLogServer()
{
	try
	{
		if ( m_pSysLogSrv )
		{
			m_pSysLogSrv->Stop();
			m_pSysLogSrv = NULL;
		}
	}
	catch(...)
	{
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////
void CGUILogic::ReRequestResourceCounters()
{
	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];

		if ( bind.bEnabled &&
			 bind.plType!=cPTNone &&
			 bind.codeVal != bind.codeMaxVal )
		{
			if ( m_pLink )
			{
				COMMANDREQUEST req(bind.codeMaxVal);
				m_pLink->AddRequest(req);
			}
		}
	}
}


void CGUILogic::SetAllValuesToZero()
{
	PortForwardingMarkInProgress();

	for(size_t i=0;i<m_binds.size();i++)
	{
		GUIBIND& bind = m_binds[i];
		bind.SetVal(0);	
		bind.SetTxtVal(_(""));
		bind.Reset();
	}

	m_tLastWANIPCheckTime = 0;
	m_strWANAddr = "";

	UpdateTaskBarIcon();
	UpdateStatusBar();
}

void CGUILogic::ShowLogDlg()
{
	try
	{
		m_pLogDlg = new CLogViewer( *this );
		m_pLogDlg->Show();
	}catch(...)
	{
	}
}

void CGUILogic::DoStartUpAction()
{
	try
	{
		m_rebootSched.bEnableSched = m_wMain.GetPropStorage().GetSchedulerEnabled();
		m_rebootSched.SchedFromString( m_wMain.GetPropStorage().GetScheduler() );
	}
	catch(...)
	{

	}

	m_stateController.Reset();

	if ( m_pLink )
	{
		COMMANDREQUEST req(cCmdGetRouterID);
		m_pLink->AddRequest(req);

		if ( m_wMain.GetPropStorage().GetEnableProcKill() &&
			 CRouterCaps::GetIntstance().HasProcKill() )
		{
			TEXTLIST lst = m_wMain.GetPropStorage().GetProcKillList();

			if ( !lst.empty() )
			{
				SendActionToRouter( m_scriptGen.GenerateProcKillScript(lst) );

				LOGENTRY entry;
				entry.strCustomEvent = _("Unused program removed");
				AddToLog(entry);
			}
		}

		if ( m_wMain.GetPropStorage().GetEnableModUnload() &&
			 CRouterCaps::GetIntstance().HasKernelMod() )
		{
			TEXTLIST lst = m_wMain.GetPropStorage().GetModUnloadList();

			if ( !lst.empty() )
			{
				SendActionToRouter( m_scriptGen.GenerateModUnloadScript(lst) );

				LOGENTRY entry;
				entry.strCustomEvent = _("Unused kernel modules unloaded");
				AddToLog(entry);
			}
		}

		if ( m_wMain.GetPropStorage().GetEnableSyslogAutoConfig() &&
			 CRouterCaps::GetIntstance().HasSyslogConfig() )
		{
			long lLevel = m_wMain.GetPropStorage().GetSyslogAutoConfigLevel();
			long lPort  = m_wMain.GetPropStorage().GetSysLogUDPPort();
			wxString strIP = GetCurIP();

			if ( !strIP.empty() )
			{
				SendActionToRouter( m_scriptGen.GenerateSyslogConfigScript(strIP, lPort, lLevel), cCmdSubCodeSyslogAutoConfig );
			}
			else
			{
				LOGENTRY entry;
				entry.strCustomEvent = _("Syslog server auto-configuration failed");
				AddToLog(entry);
			}
		}

		if ( CRouterCaps::GetIntstance().HasTCPNATSettings() )
		{
			if ( m_wMain.GetPropStorage().GetEnableAdvancedOpt() )
			{
				STRINGPAIRLIST lst =  m_wMain.GetPropStorage().GetAdvancedOptList();

				//Do not send nat table size, if nat table optimization turned on
				if ( m_wMain.GetPropStorage().GetEnableNATSizeOpt() )
				{
					STRINGPAIRLIST::iterator iCur;
					for (iCur=lst.begin();iCur!=lst.end();++iCur)
					{
						const STRINGPAIR& pair = *iCur;

						if ( pair.strLabel==wxT("ip_conntrack_max") )
						{
							lst.erase(iCur);
							break;
						}
					}
				}

				LOGENTRY entry;
				entry.strCustomEvent = _("Custom optimization settings sent to route");

				if ( lst.empty() )
				{
					entry.strCustomEvent = _("Error. Custom optimization settings is empty");
				}
				else
				{
					wxString str = m_scriptGen.GenerateAdvancedOptScript( lst );
					SendActionToRouter(str);
				}

				AddToLog(entry);
			}
			else
			{
				if ( m_wMain.GetPropStorage().GetEnableNATOpt() )
				{
					wxString str = m_scriptGen.GenerateNATOptScript();
					SendActionToRouter(str);

					LOGENTRY entry;
					entry.strCustomEvent = _("NAT settings optimized");
					AddToLog(entry);
				}

				if ( m_wMain.GetPropStorage().GetEnableTCPOpt() )
				{
					wxString str = m_scriptGen.GenerateTCPScript();
					SendActionToRouter(str);

					LOGENTRY entry;
					entry.strCustomEvent = _("TCP settings optimized");
					AddToLog(entry);
				}

				if ( m_wMain.GetPropStorage().GetEnableICMPOpt() )
				{
					wxString str = m_scriptGen.GenerateICMPScript();
					SendActionToRouter(str);

					LOGENTRY entry;
					entry.strCustomEvent = _("ICMP settings optimized");
					AddToLog(entry);
				}

				if ( m_wMain.GetPropStorage().GetEnableP2PNetworkOpt() )
				{
					wxString str = m_scriptGen.GenerateP2PScript();
					SendActionToRouter(str);

					LOGENTRY entry;
					entry.strCustomEvent = _("P2P settings optimized");
					AddToLog(entry);
				}

				if ( m_wMain.GetPropStorage().GetEnableWWWOpt() )
				{
					wxString str = m_scriptGen.GenerateWWWScript();
					SendActionToRouter(str);

					LOGENTRY entry;
					entry.strCustomEvent = _("WWW/FTP settings optimized");
					AddToLog(entry);
				}
			}
		}

		ExecutePortForward();

		ExecuteMACBlock();

		if ( m_wMain.GetPropStorage().GetEnableStartUpScript() )
		{
			wxString str;

			try
			{
				wxString strScriptFileName = m_wMain.GetPropStorage().GetStartUpScriptFileName();

				std::string strIn;
				std::ifstream ifs ( strScriptFileName.ToAscii(), std::ifstream::in );

				while (ifs.good())
				{
					strIn+=ifs.get();
				}

				ifs.close();

				str = wxString::FromAscii( strIn.c_str() );

				if ( !str.empty() )
				{
					SendActionToRouter(str);
				}
			}
			catch(...)
			{
			}

			LOGENTRY entry;

			if ( !str.empty() )
			{
				entry.strCustomEvent = _("Router Startup Script sent");
			}
			else
			{
				entry.strCustomEvent = _("Router Startup Script failed");
			}

			AddToLog(entry);
		}

		if ( CRouterCaps::GetIntstance().HasTCPNATSettings() )
		{
			RequestTCPTimeOuts();
		}

		RequestRouterVersion();

		ReRequestResourceCounters();
	}
}

void CGUILogic::RequestRouterVersion()
{
	assert(m_pLink);

	if ( m_pLink )
	{
		//Request tcp time outs
		COMMANDREQUEST req( cCmdGetVersion );
		m_pLink->AddRequest(req);
	}
}

void CGUILogic::RequestTCPTimeOuts()
{
	assert(m_pLink);

	if ( m_pLink )
	{
		//Request tcp time outs
		COMMANDREQUEST req( cCmdGetTCPTimeOuts );
		m_pLink->AddRequest(req);
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////
//Log Sorting
bool SortTime( const LOGENTRY& a, const LOGENTRY& b )
{
	return  CGUILogic::m_bSortAsc?a.tEvent>b.tEvent:a.tEvent<b.tEvent;
}

bool SortSrc( const LOGENTRY& a, const LOGENTRY& b )
{
	return  CGUILogic::m_bSortAsc?a.plType>b.plType:a.plType<b.plType;
}

bool SortEvent( const LOGENTRY& a, const LOGENTRY& b )
{
	if ( a.strCustomEvent.IsEmpty() && b.strCustomEvent.IsEmpty() )
	{
		return  CGUILogic::m_bSortAsc?a.strat>b.strat:a.strat<b.strat;	
	}
	else
	{
		return  CGUILogic::m_bSortAsc?a.strCustomEvent>b.strCustomEvent:a.strCustomEvent<b.strCustomEvent;
	}
}


void CGUILogic::SortLog( int nField )
{
	m_nLogSortCol = nField;

	switch (nField)
	{
		case cLogColumnTime:
		{
			std::sort( m_log.begin(), m_log.end(), SortTime);
		}
		break;

		case cLogColumnSrc:
		{
			std::sort( m_log.begin(), m_log.end(), SortSrc);
		}
		break;

		case cLogColumnEvent:
		{
			std::sort( m_log.begin(), m_log.end(), SortEvent);
		}
		break;
	}
}

time_t CGUILogic::GetUpdateIntervl(cCmdCode cCode)
{
	time_t tmRes(m_tUpdateInterval);

	if ( cCmdConntrTableEntries==cCode )
		tmRes*=10;

	return tmRes;
}

void CGUILogic::OnFilterLoaded(cSearchOwnerID cID, FILTERS& filters)
{
	if ( cID == cSearchOwnerIDConntrac )
	{
		IPLIST  ipList = CSysDepAPI::GetAddressList();

		if ( !ipList.empty() )
		{
			FILTER filter;
			filter.strFilterName = _("From My PC");
			filter.bSysDef = true;
			
			FILTERFIELDRULE rule;
			rule.iFieldID = 1;
			rule.rule = cFilterRuleRegExp;

			for(size_t i=0;i<ipList.size();i++)
			{
				if ( !rule.strVal.IsEmpty() )
					rule.strVal+=wxT("|");

				rule.strVal+= wxString::FromAscii( ipList[i].c_str() );
			}
			
			filter.filterDef.push_back(rule);
			
			filters.push_back(filter);
		}
	}

	if ( cID == cSearchOwnerIDSysLog )
	{
		FILTER filter;
		filter.strFilterName = _("From Router");
		filter.bSysDef = true;
			
		FILTERFIELDRULE rule;
		rule.iFieldID = 2;
		rule.rule = cFilterRuleEq;

		rule.strVal= m_params.strIP;

		filter.filterDef.push_back(rule);
		filters.push_back(filter);
	}
}

//TODO: optimization needed
void CGUILogic::CheckScheduling()
{
	wxDateTime tNow( time(NULL) );
	unsigned short h = tNow.GetHour();

	if ( h!=m_wLastMACExecTime )
	{
		ExecuteMACBlock(false);
	}
}

void CGUILogic::ExecuteMACBlock(bool bLogEvent)
{
	if ( CRouterCaps::GetIntstance().HasMACFilter() )
	{
		MACBLOCK lstWhite;
		MACBLOCK lstBlack;

		try
		{
			lstWhite  = m_wMain.GetPropStorage().GetMACList( cMACBlockRuleIdWL  );
			lstBlack  = m_wMain.GetPropStorage().GetMACList( cMACBlockRuleIdBL  );
		}catch(...) {}

		bool bLog = m_wMain.GetPropStorage().GetLogBlockedMAC();

		//if ( !lstWhite.list.empty() || !lstBlack.list.empty() )
		{
			wxString str = m_scriptGen.GenerateMACBlockScript(lstWhite, lstBlack, bLog);
			SendActionToRouter(str);

			if ( bLogEvent )
			{
				LOGENTRY entry;
				entry.strCustomEvent = _("MAC access list applied");
				AddToLog(entry);
			}

			wxDateTime tNow(time(NULL));
			m_wLastMACExecTime = tNow.GetHour();

			GUIBIND bind;
			if ( GetBind( bind, cCmdMACBlockExecuted ) )
			{
				COMMANDRESPONSE cmd;
				cmd.code  = cCmdMACBlockExecuted;
				cmd.lData = CODE_SCRIPT_EXEC_OK;

				bind.GetBinding()->OnResponse(cmd);
			}
		}
	}
}

void CGUILogic::SendLogToSMTP()
{
	wxString strSMTPServer = m_wMain.GetPropStorage().GetSMTPServer();
	unsigned short wSMTPPort = m_wMain.GetPropStorage().GetSMTPServerPort();
	wxString strSMTPUser = m_wMain.GetPropStorage().GetSMTPUser();
	wxString strSMTPPassw = m_wMain.GetPropStorage().GetSMTPPassw();
	wxString strSMTPFrom = m_wMain.GetPropStorage().GetSMTPFrom();
	wxString strSMTPTo = m_wMain.GetPropStorage().GetSMTPTo();
	bool bSendAppLog = m_wMain.GetPropStorage().GetSendAppLog();
	bool bSendSysLog = m_wMain.GetPropStorage().GetSendSysLog();
	long lSysLogLevelToSend = m_wMain.GetPropStorage().GetSyslogSendLevel();
	time_t tSend = m_wMain.GetPropStorage().GetSendPeriod();

	time_t tNow = time(NULL);

	wxString strAppLog, strSysLog;

	if ( strSMTPServer.empty() || strSMTPFrom.empty() || strSMTPTo.empty() )
		return;

	if ( !bSendAppLog && !bSendSysLog )
		return;

	if ( tNow - tSend>m_tLastSMTPSendTime )
	{
		m_tLastSMTPSendTime = tNow;

		if ( bSendAppLog )
		{
			wxString str;

			for ( size_t i=0;i<m_log.size();i++ )
			{
				LOGENTRY& entry = m_log[i];

				if (entry.bSent) continue;

				entry.bSending = true;

				if (!str.empty()) str+=wxT("\n");

				wxDateTime dt(entry.tEvent);
				str+= dt.Format(_("%c"));

				str+=wxT("\t");

				if ( entry.strCustomEvent.IsEmpty() )
				{
					str+= CGUILogic::PanelToName( entry.plType );
				}
				else
				{
					str+= _("Application");
				}

				str+=wxT("\t");

				if ( entry.strCustomEvent.IsEmpty() )
				{
					str+= CGUILogic::StratToName( entry.strat );
				}
				else
				{
					str+= entry.strCustomEvent;
				}
			}

			strAppLog = str;
		}

		if ( bSendSysLog )
		{
			wxString str;

			for ( size_t i=0;i<m_syslogCopy.size();i++ )
			{
				SYSLOGMSG& entry = m_syslogCopy[i];

				entry.bSending = true;

				if ( lSysLogLevelToSend >= entry.iPrio )
				{
					if (!str.empty()) str+=wxT("\n");
					str+=entry.strRaw;
				}
			}

			strSysLog = str;
		}
	}

	try
	{
		if ( !strAppLog.empty() || !strSysLog.empty()  )
		{
			if ( m_smtp )
			{
				m_smtp->Destroy();
				delete m_smtp;
				m_smtp = NULL;
			}

			m_smtp = new wxSMTP();
		}

		if ( m_smtp && (!strAppLog.empty() || !strSysLog.empty()) )
		{
			wxString strFinal;

			if ( !strAppLog.empty() )
			{
				strFinal+=_("Application LOG:");
				strFinal+=wxT("\n");
				strFinal+=strAppLog;
				strFinal+=wxT("\n");
			}

			if ( !strSysLog.empty() )
			{
				strFinal+=_("Syslog LOG:");
				strFinal+=wxT("\n");
				strFinal+=strSysLog;
			}

			wxEmailMessage *msg = new wxEmailMessage(	_("RouterCmd LOG Files "),
														strFinal,
														strSMTPFrom );
			msg->AddTo(strSMTPTo);
			m_smtp->SetHost(strSMTPServer, wSMTPPort);
			m_smtp->SetUser( strSMTPUser, strSMTPPassw); 
			m_smtp->Send(msg);

		}
	}
	catch(...)
	{
		assert(0);
	}
}

void CGUILogic::OnSMTPSendLogFailed(const wxString& str)
{
	LOGENTRY entry;
			
	entry.strCustomEvent+=_("SMTP Error");
	entry.strCustomEvent+=wxT(": ");
	entry.strCustomEvent+=str;
	AddToLog(entry);	
}

void CGUILogic::OnSMTPSendLogSucceeded()
{
	//Mark sent items
	for ( size_t i=0;i<m_log.size();i++ )
	{
		LOGENTRY& entry = m_log[i];

		if (entry.bSent) continue;

		if ( entry.bSending )
			entry.bSent = true;
	}

	//remove sent items
	SYSLOGMSGSTORE	syslogTmp;

	for ( size_t i=0;i<m_syslogCopy.size();i++ )
	{
		SYSLOGMSG& entry = m_syslogCopy[i];

		if ( !entry.bSending )
		{
			syslogTmp.push_back(entry);
		}
	}
	
	m_syslogCopy = syslogTmp;

}

void CGUILogic::OnTextCMD( CMDCODE code, wxString& str )
{
	if ( cCmdGetIPConfig == code )
	{
		wxString strRes = _("Router IP settings:");
		strRes+=wxT("\n\n");

		if ( !m_strWANAddr.empty() )
		{
			strRes+=_("Router WAN (external) IP:");
			strRes+=wxT(" ");
			strRes+= wxString::FromAscii( m_strWANAddr.c_str() );
			strRes+= wxT("\n\n");
		}

		strRes+=str;

		strRes+=wxT("\n");
		strRes+= _("Local IP settings:");
		strRes+=wxT("\n\n");
		wxString strLocal =CSysDepAPI::GetLocalIPSettings();
		strLocal.Replace(wxT("\n\n"),wxT("\n"));
		strLocal.Replace(wxT("\r"),wxT(""));
		strRes+=strLocal;

		strRes+=wxT("\n");
		strRes+= _("Local routing table:");
		strRes+=wxT("\n\n");
		strLocal =CSysDepAPI::GetLocalRoutingTable();
		strLocal.Replace(wxT("\n\n"),wxT("\n"));
		strLocal.Replace(wxT("\r"),wxT(""));
		strRes+=strLocal;

		str = strRes;
	}
}

void CGUILogic::OnWANAddress( const RTSTRING& str)
{
	if ( m_strWANAddr!=str )
	{
		m_strWANAddr = str;

		LOGENTRY entry;
		entry.strCustomEvent = _("Router WAN (external) IP:");
		entry.strCustomEvent+=wxT(" ");
		entry.strCustomEvent+= wxString::FromAscii( str.c_str() );
		AddToLog(entry);

	}
}

//Linux version 2.4.17_mvl21-malta-mips_fp_le (root@Jackal) (gcc version 2.95.3 20
//010315 (release/MontaVista)) #96 Fri Sep 30 15:29:07 CST 2005
void  CGUILogic::OnRouterVersion(const RTSTRING& str)
{
	m_strVersion = str;

	//Looks like it's common problem...
	EnableCmd(cCmdFlushRTTable, true);
}

void CGUILogic::OnScriptResult( CMDSUBCODE subCmd, const RTSTRING& str )
{
	DestroyProgress();

	if ( std::string::npos!=str.find("Couldn't find match `mac'") )
	{
		GUIBIND bind;
		if ( GetBind( bind, cCmdMACBlockExecuted ) )
		{
			COMMANDRESPONSE cmd;
			cmd.code  = cCmdMACBlockExecuted;
			cmd.lData = CODE_SCRIPT_EXEC_FAIL;

			bind.GetBinding()->OnResponse(cmd);

			LOGENTRY entry;
			entry.strCustomEvent = _("Block mac address failed: Router doesn't have required 'mac' module");
			AddToLog(entry);
		}
	}

	if ( std::string::npos!=str.find("killall: No such file or directory") )
	{
		COMMANDREQUEST req( cCmdGetProcessList );
		m_pLink->AddRequest(req);
	}

	if (subCmd==cCmdSubCodePortForwarding)
	{
		PortForwardingFinished();
	}

	if ( subCmd==cCmdSubCodeSyslogAutoConfig )
	{
		LOGENTRY entry;
		
		if ( std::string::npos!=str.find("syslogd: No such file or directory") )
		{
			entry.strCustomEvent = _("Syslog server auto-configuration finished");
		}
		else
		{
			entry.strCustomEvent = _("Syslog server auto-configuration failed");
		}

		AddToLog(entry);
	}
}

void CGUILogic::OnProcList( const RTSTRING& str )
{
	if ( m_wMain.GetPropStorage().GetEnableProcKill() )
	{
		TEXTLIST lst = m_wMain.GetPropStorage().GetProcKillList();

		if ( !lst.empty() )
		{
			PROCTABLEWITHHEADER table = CLogicManager::ParseTableWithHeaders( str, 5 );

			wxString strScript;

			for ( size_t i=0;i<table.rows.size();++i)
			{
				wxString strPocName = CLogicManager::GeValFromTable(table, "Command", i, 0 );
				size_t  stPID = CLogicManager::GeValFromTableInt(table, "PID", i, 0 );

				if ( stPID )
				{
					for ( size_t j=0;j<lst.size();++j )
					{
						wxString strProc( wxString::FromAscii(lst[j].c_str()) );

						if ( wxNOT_FOUND!=strPocName.Find(strProc) )
						{
							strScript+=wxT("kill -s 9 ");
							strScript+=wxString::Format(wxT("%d"), stPID);
							strScript+=wxT(TELNET_ENTER);
						}
					}
				}
			}

			if ( !strScript.empty() )
			{
				SendActionToRouter( strScript );
			}

		}
	}
}

void CGUILogic::OnWirelessCaps( const RTSTRING& str )
{
	EnableCmd(cCmdGetWirelessStats, !str.empty() );

	if ( str.empty() )
	{
		m_wMain.RemoveTab( cCmdGetWirelessStats );
	}
}

wxString CGUILogic::GetCurIP()
{
	wxString strRouterIP = GetRouterIP();
	wxString strIP = CSysDepAPI::GetRelevantLocalIPAddress( strRouterIP );

	return strIP;
}

void CGUILogic::ProcessPortForwarding( const PORTFORWARDLIST& lst )
{
	m_wMain.GetPropStorage().SetPortForwardLst(lst);

	m_lstPortForward = lst;
		
	ExecutePortForward();
}

void  CGUILogic::ToglePFRule( const wxString& strRuleName)
{
	if ( IsConnected() )
	{
		unsigned short wPortLow = m_wMain.GetPropStorage().GetAutoPortForwardLowPort();
		TEXTLIST lstAllow = m_wMain.GetPropStorage().GetAllowPFPolicy();
		TEXTLIST lstBlock = m_wMain.GetPropStorage().GetBlockPFPolicy();
		bool bAllowPolicy = m_wMain.GetPropStorage().GetEnableAllowPFPolicy();

		for(size_t i=0;i<m_lstPortForward.size();i++)
		{
			PORTFORWARDRULE& rule = m_lstPortForward[i];

			if ( rule.strLabel == strRuleName )
			{
				bool bCur = rule.bEnabled;

				rule.bAutoRule = true;

				if ( rule.bEnabled )
				{
					//Have to disable PF rule
					//---------------------------

					//Block all, except listed
					if ( bAllowPolicy )
					{
						//If was in list, remove from allowed prog list
						if ( IsInList( lstAllow, rule.strProcName) )
						{
							RemoveFromList(lstAllow, rule.strProcName);
							m_wMain.GetPropStorage().SetAllowPFPolicy(lstAllow);
						}
					}
					else
					{
						//Block policy enabled, add program to block list
						lstBlock.push_back((const char*)rule.strProcName.ToUTF8());
						m_wMain.GetPropStorage().SetBlockPFPolicy(lstBlock);
					}
				}
				else
				{
					//Have to enable PF rule
					//----------------------------------

					if ( bAllowPolicy )
					{
						lstAllow.push_back((const char*)rule.strProcName.ToUTF8());
						m_wMain.GetPropStorage().SetAllowPFPolicy(lstAllow);
					}
					else
					{
						//Block listed policy
						//If was in list, remove it from list to enable PF
						if ( IsInList( lstBlock, rule.strProcName) )
						{
							RemoveFromList(lstBlock, rule.strProcName);
							m_wMain.GetPropStorage().SetBlockPFPolicy(lstBlock);
						}
					}
				}

				FilterPFRule(rule, lstAllow, lstBlock, bAllowPolicy, wPortLow);

				//Failed to togle it using rules based system,
				//Convert rule to manual rule
				if ( bCur == rule.bEnabled )
				{
					rule.bAutoRule = false;
					rule.bEnabled = !rule.bEnabled;
				}

				//Done
				rule.bRuleInProgress = true;
				break;
			}
		}
	}
}

void CGUILogic::ProcessAutoPortForwarding(bool bAuto)
{
	m_wMain.GetPropStorage().SetAutoPortForward(bAuto);

	m_bAutoPortForward = bAuto;

	ExecutePortForward();
}

void CGUILogic::PortForwardingMarkInProgress()
{
	for(size_t i=0;i<m_lstPortForward.size();i++)
	{
		PORTFORWARDRULE& rule = m_lstPortForward[i];

		if ( rule.bAutoRule && rule.bEnabled==false)
		{
			continue;
		}

		if ( IsConnected() )
		{
			rule.bRuleInProgress = true;
		}
	}
}

void CGUILogic::PortForwardingFinished()
{
	LOGENTRY entry;
	entry.strCustomEvent = _("Port Forward rules applied");
	AddToLog(entry);

	for(size_t i=0;i<m_lstPortForward.size();i++)
	{
		PORTFORWARDRULE& rule = m_lstPortForward[i];
		rule.bRuleInProgress = false;
	}

	//if ( !m_lstPortForward.empty() )
	{
		UpdatePortForwardGUI();
	}
}

void CGUILogic::ExecutePortForward()
{
	if ( CRouterCaps::GetIntstance().HasPortForward() )
	{
		PortForwardingMarkInProgress();

		wxString str = m_scriptGen.GeneratePortForwardScript(m_lstPortForward);

		if ( !str.empty() )
		{
			SendActionToRouter(str, cCmdSubCodePortForwarding);
		}

		UpdatePortForwardGUI();
	}
}

bool CGUILogic::EraseDeletedPortForwardRules()
{
	bool bRes(false);

	//Erase deleted rules
	PORTFORWARDLIST::iterator iCur;

	for ( iCur=m_lstPortForward.begin();iCur!=m_lstPortForward.end();iCur++)
	{
		PORTFORWARDRULE& rule = *iCur;

		if ( rule.bAutoRule && rule.bAutoMustBeDeleted )
		{
			m_lstPortForward.erase(iCur);

			bRes = true;

			EraseDeletedPortForwardRules();
			break;
		}
	}

	return bRes;
}

//Don't have to build erase list because of -flush
void CGUILogic::ProcessAutoForwarding()
{
	bool bErased = EraseDeletedPortForwardRules();

	PROTOLIST pl = CSysDepAPI::GetTCPProtoList( GetRouterIP() );

	bool bHaveToUpdate(false);
	unsigned short wPortLow = m_wMain.GetPropStorage().GetAutoPortForwardLowPort();

	TEXTLIST lstAllow = m_wMain.GetPropStorage().GetAllowPFPolicy();
	TEXTLIST lstBlock = m_wMain.GetPropStorage().GetBlockPFPolicy();
	bool bAllowPolicy = m_wMain.GetPropStorage().GetEnableAllowPFPolicy();

	for(size_t i=0;i<m_lstPortForward.size();i++)
	{
		PORTFORWARDRULE& rule = m_lstPortForward[i];

		bool bExist = false;

		for ( PROTOLIST::iterator iCur=pl.begin();iCur!=pl.end();iCur++ )
		{
			PROTOENTRY& entry = *iCur;

			//process exist
			if ( rule.wPortFrom == entry.wPort &&
				 rule.strDst == entry.strIP )
			{
				bExist = true;
			}

			//Local port already forwarded
			if ( entry.wPort == rule.wPortTo &&
				 ( entry.proto == rule.proto || rule.proto==cPortFrorwardProtoAny) )
			{
				//Change name
				if ( rule.bAutoRule && entry.strOwnerName!=rule.strLabel )
				{
					bHaveToUpdate = true;
					rule.strLabel = entry.strOwnerName;
				}

				pl.erase( iCur );
				break;
			}

			//Rule with this name already exists
			if ( entry.strOwnerName==rule.strLabel )
			{
				entry.strOwnerName+=wxT("#");
			}

			//Remote port already forwarded
			if ( entry.wPort == rule.wPortFrom &&
				 ( entry.proto == rule.proto || rule.proto==cPortFrorwardProtoAny) )
			{
				pl.erase( iCur );
				break;
			}
		}

		if ( rule.bAutoRule )
		{
			//Process exited, disable rule,
			//rule will be deleted on next iteration
			if ( !bExist )
			{
				rule.bEnabled = false;
				rule.bAutoMustBeDeleted = bHaveToUpdate = true;
			}
			else
			{
				bool bEnabled = rule.bEnabled;

				/*
		 		if ( rule.wPortFrom < wPortLow ||
					!m_bAutoPortForward || !IsConnected() )
				{
					rule.bEnabled = false;
				}
				else
				{
					rule.bEnabled = true;
				}*/

				FilterPFRule(rule, lstAllow, lstBlock, bAllowPolicy, wPortLow);

				//Rule policy changed
				if (bEnabled!=rule.bEnabled)
				{
					bHaveToUpdate = true;
				}
			}
		}
	}

	if ( !pl.empty() || bHaveToUpdate )
	{
		bHaveToUpdate = true;

		for(size_t i=0;i<pl.size();i++)
		{
			PROTOENTRY& entry = pl[i];

			PORTFORWARDRULE rule;

			rule.strProcName = entry.strProcName;
			if ( rule.strProcName.empty() )
			{
				rule.strProcName = entry.strOwnerName;
			}

			rule.bAutoRule = true;
			rule.strLabel = entry.strOwnerName;
			rule.proto = entry.proto;
			rule.strDst = entry.strIP;
			rule.wPortFrom=rule.wPortFromTo=rule.wPortTo = entry.wPort;
			//rule.bEnabled = rule.wPortFrom>=wPortLow && m_bAutoPortForward && IsConnected();

			FilterPFRule(rule, lstAllow, lstBlock, bAllowPolicy, wPortLow);

			m_lstPortForward.push_back(rule);
		}

		ExecutePortForward();
	}

	if ( bHaveToUpdate || bErased )
	{
		UpdatePortForwardGUI();
	}
}

void CGUILogic::FilterPFRule(PORTFORWARDRULE& rule, const TEXTLIST& lstAllow,
							 const TEXTLIST& lstBlock, bool bAllowPolicy, unsigned short wPortLow)
{
	if ( rule.bAutoRule )
	{
		rule.bEnabled = false;

		if ( IsConnected() && m_bAutoPortForward )
		{
			//Low bound is absolute rule
			rule.bEnabled = rule.wPortFrom>=wPortLow;

			if ( rule.bEnabled )
			{
				if ( bAllowPolicy )
				{
					rule.bEnabled = IsInList( lstAllow, rule.strProcName);
				}
				else
				{
					rule.bEnabled = !IsInList( lstBlock, rule.strProcName);
				}
			}
		}
	}
}

bool CGUILogic::IsInList(const TEXTLIST& lst, const wxString& strProcName)
{
	bool bRes(false);

	wxString strProc = strProcName.Lower(), strTmp;

	for (size_t i=0;i<lst.size();++i)
	{
		strTmp = wxString::FromUTF8( lst[i].c_str() ).Lower();

		if ( wxNOT_FOUND!=strProc.Find(strTmp) )
		{
			bRes = true;
			break;
		}
	}

	return bRes;
}

void CGUILogic::RemoveFromList(TEXTLIST& lst, const wxString& strProcName)
{
	TEXTLIST::iterator iCur = lst.begin();

	wxString strProc = strProcName.Lower(), strTmp;

	for(;iCur != lst.end();++iCur)
	{
		strTmp = wxString::FromUTF8( (*iCur).c_str() ).Lower();

		if ( wxNOT_FOUND!=strProc.Find(strTmp) )
		{
			lst.erase(iCur);
			break;
		}
	}
}

//cCmdPFTableUpdated
void CGUILogic::UpdatePortForwardGUI()
{
	GUIBIND bind;

	if ( GetBind(bind, cCmdPFTableUpdated) )
	{
		bind.SetVal(0);
	}
}

unsigned long CGUILogic::GetConntrackMAX()
{
	unsigned long  iVal(1);
	GUIBIND bind;

	if ( GetBind(bind, cCmdConntracMax) )
	{
		iVal = bind.GetMaxVal();
	}

	return iVal;
}

wxString CGUILogic::GeneratePingScript()
{
	wxString strScript, strTmp;

	wxString strSiteMap[] = { wxT("8.8.8.8") };/* , wxT("yahoo.com"), wxT("bing.com"),
	wxT("facebook.com"), wxT("myspace.com"), wxT("linkedin.com"),
	wxT("cnn.com"), wxT("cbsnews.com"), wxT("abcnews.go.com") };*/

	for(size_t i=0;i< sizeof(strSiteMap)/sizeof(strSiteMap[0]);++i)
	{
		strTmp=CMD_PING;
		strTmp+=strSiteMap[i];

		if ( !strScript.empty() ) strScript+=wxT(";");
		strScript+=strTmp;
	}

	return strScript;
}

void CGUILogic::DestroyProgress()
{
	if ( m_pProgress )
	{
		delete m_pProgress;
		m_pProgress = NULL;
	}
}

void  CGUILogic::PrepareToProgramExit()
{
	if (m_pTheTaskBarIcon)
	{
		delete m_pTheTaskBarIcon;
		m_pTheTaskBarIcon = NULL;
	}
}